﻿using System;
using System.Collections.Generic;
using System.Text;

using NHibernate;
using SNARF.Framework.ActiveRecord;
using NHibernate.Classic;

namespace SNARF.Framework.ActiveRecord
{
    public class ActiveRecordBase<T> : NHibernate.Classic.ILifecycle
    {
        protected ActiveRecordBase() { }

        #region UtilityMethods
        private static void Save(Object obj)
        {       

            SessionManager.CurrentSession.SaveOrUpdate(obj);            
            Flush();
        }

        private static void Insert(Object obj)
        {
            SessionManager.CurrentSession.Save(obj);
            Flush();
        }

        private static void Update(Object obj)
        {
            SessionManager.CurrentSession.Update(obj);
            Flush();
        }

        private static void Delete(Object obj)
        {
            SessionManager.CurrentSession.Delete(obj);
            Flush();
        }

        private static void Flush()
        {
            SessionManager.CurrentSession.Flush();
        }

        private static string GetFromClause()
        {
            string entityName = typeof(T).Name;

            return "SELECT " + entityName + " FROM " + entityName + " as " + entityName + " WHERE ";
        }

        private static string GetFromAndWhere(string whereClause)
        {
            return GetFromClause() + whereClause;
        }

        private static IList<T> ListQuery(IQuery query)
        {
            return new List<T>(query.List<T>());
        }
        //private static void EnumerableQuery(IQuery query)
        //{
        //    return query.Enumerable<T>();
        //}

        private static readonly string sortString = " ORDER BY ";

        #endregion

        #region Retrieval static methods

        [Obsolete("Use FindByID instead")] 
        public static T Get(object id)
        {
            return FindByID(id); 
        }

        [Obsolete("Use one of the find methods or FindByQuery instead")] 
        public static IList<T> GetList(string hqlQuery)
        {
            return FindByQuery(hqlQuery);
        }

        public static T FindByID(object id)
        {
            return (T)SessionManager.CurrentSession.Get<T>(id);            
        }


        public static IList<T> FindAll()
        {
            return FindAll("");
        }

        public static IList<T> FindAll(string sortClause)
        {
            return Find(" 0=0 ",sortClause);
        }
       


        public static IList<T> Find(string whereClause, string sortClause)
        {
            return Find(whereClause, sortClause, new object[] { });
        }

        public static IList<T> Find(string whereClause, string sortClause, params object[] parameters)
        {
            return Find(whereClause, sortClause, false, -1, -1, parameters);
        }

        public static IList<T> Find(string whereClause, string sortClause, int pageNumber, int pageSize, params object[] parameters)
        {
            return Find(whereClause, sortClause, true, pageNumber, pageSize, parameters);
        }

        private static IList<T> Find(string whereClause, string sortClause, bool shouldPage, int pageNumber, int pageSize, params object[] parameters)
        {
            string hql = GetFromAndWhere(whereClause);
            if (sortClause != string.Empty)
                hql += " " + sortString + " " + sortClause;

            IQuery query = SessionManager.CurrentSession.CreateQuery(hql);

            int i = 0;
            foreach (object param in parameters)
            {
                query.SetParameter(i, param);
                i++;
            }

            if (shouldPage)
            {
                query.SetMaxResults(pageSize);
                query.SetFirstResult(pageSize * pageNumber);
            }

            
            return ListQuery(query);
        }
       

        public static IList<T> FindByQuery(string fullHqlQuery)
        {
            return FindByQuery(SessionManager.CurrentSession.CreateQuery(fullHqlQuery));
        }

        public static IList<T> FindByQuery(IQuery queryObject)
        {
            return ListQuery(queryObject);
        }

        public static void Save(T obj)
        {
            Save(obj);
        }
        public static void Insert(T obj)
        {
            Insert(obj);
        }
        public static void Update(T obj)
        {
            Update(obj);
        }

        public static void DeleteByKey(object key)
        {
            string typeName = typeof(T).Name;
            SessionManager.CurrentSession.Delete(FindByID(key));
            SessionManager.CurrentSession.Flush();            
        }

        #endregion

        #region Instance Methods

        public virtual bool IsNew
        {
            get { return SessionManager.CurrentSession.GetIdentifier(this) != null; }
        }

        public virtual void Save()
        {
            Save(this);
        }

        public virtual void Insert()
        {
            Insert(this);
        }

        public virtual void Update()
        {
            Update(this);
        }

        public virtual void Delete()
        {
            Delete(this);
        }


        public virtual void Refresh()
        {
            SessionManager.CurrentSession.Refresh(this);
            Flush();
        }

        #endregion

        #region callback methods

        /// <summary>
        /// Called when NHibernate inserts or updates an object
        /// </summary>
        /// <param name="session">the current ISession</param>
        /// <param name="isInsertingNew">whether NHibernate is saving this as a new object</param>
        public virtual void OnSaving() {  }
        public virtual void OnLoading( object id) { }
        public virtual void OnDeleting() { }
        


        #endregion



        #region ILifecycle Members

        public LifecycleVeto OnDelete(ISession s)
        {            
            this.OnDeleting();
            return LifecycleVeto.NoVeto;
        }

        public void OnLoad(ISession s, object id)
        {
             this.OnLoading(id);
        }

        public LifecycleVeto OnSave(ISession s)
        {
            this.OnSaving( );
            return LifecycleVeto.NoVeto;
        }

        public LifecycleVeto OnUpdate(ISession s)
        {
            this.OnSaving( );
            return LifecycleVeto.NoVeto;
        }

        #endregion
    }
}
